package com.ykq.sso.config;

import com.alibaba.fastjson.JSON;
import com.ykq.core.util.JWTUtil;
import com.ykq.core.vo.R;
import com.ykq.sso.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.annotation.Resource;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: 闫克起
 * @Description:
 * @Date: Create in 11:18 2024/8/12
 */
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    //把输入的密码经过密码加密器加密后 数据库中的密码对比
    @Bean
    public PasswordEncoder passwordEncoder(){
        PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
        return passwordEncoder;
    }

    @Resource   //@Autowire  @Resource的区别? @Autowired只会按照类型注入 按照名称注入@Qu  @Resource 按照类型和名称主儿
    private MyUserDetailService userDetailService;
    //配置文件中的账号和密码失效
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //把自定义的过滤器放在UsernamePasswordAuthorizationFilter之前
        http.formLogin()
                .loginProcessingUrl("/sso/login")
                .successHandler(successHandler())
                .failureHandler(failureHandler())
                .permitAll(); //上面的请求路径无需认证
        //放行那些资源
//        http.authorizeRequests().antMatchers("/fail.html").permitAll();
        http.csrf().disable();//禁用跨域伪造请求的过滤器
        http.cors();//security登录允许跨域
        //除了上的请求,其他请求都需要认证
        http.authorizeRequests().anyRequest().authenticated();
    }
    //权限不足时的处理函数


    //登录失败的处理函数
    private AuthenticationFailureHandler failureHandler(){

        return (httpServletRequest, httpServletResponse, e) -> {
            httpServletResponse.setContentType("application/json;charset=utf-8");
            PrintWriter writer = httpServletResponse.getWriter();
            R<Object> r = R.error("登录失败");
            String jsonString = JSON.toJSONString(r);
            writer.println(jsonString);
            writer.flush();
            writer.close();
        };

    }

    //登录成功的处理函数
    private AuthenticationSuccessHandler successHandler(){
        return (httpServletRequest, httpServletResponse, authentication) -> {
            //设置响应的编码
            httpServletResponse.setContentType("application/json;charset=utf-8");
            //获取输出对象
            PrintWriter writer = httpServletResponse.getWriter();
            //返回json数据即可
            Map<String,Object> map=new HashMap<>();
            map.put("username",authentication.getName());
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            //获取权限
            List<String> collect = authorities.stream().map(item -> item.getAuthority()).collect(Collectors.toList());

            map.put("permissions",collect);
            String token = JWTUtil.createToken(map);
            redisTemplate.opsForValue().set("login:"+token,"",24, TimeUnit.HOURS);
            //返回一个统一的json对象
            R<Object> ok = R.ok(token);
            //转换为json字符串
            String jsonString = JSON.toJSONString(ok);
            //servlet
            writer.println(jsonString);
            writer.flush();
            writer.close();
        };
    }
}
